package com.example.connor.nfc_smartlock_androidapp;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.nfc.cardemulation.HostApduService;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;

import java.io.IOException;
import java.util.Arrays;

/*
This service handles card emualtion for the app works on mifare desfire standards
see res/xml/aid_list.xml for supported AIDs

 */
public class TagEmulationService extends HostApduService {
    ///AID for our reader
    private static final String LOCKER_READER_AID = "F062636B6A636B686B72730A";
    ///ISO-DEP command header for selecting an AID
    private static final String SELECT_APDU_HEADER = "00A40400";
    ///ISO-DEP command header for sending the Key
    private static final String SEND_KEY_APDU_HEADER = "00DA014B";
    ///ISO-DEP command header for removing the Key
    private static final String REMOVE_KEY_APDU_HEADER = "00DA014F";
    ///ISO-DEP command header for an Access Denied Error
    private static final String ERROR_ZERO_APDU_HEADER = "00DA01E0";
    ///ISO-DEP command header for a Locker Unavailable Error
    private static final String ERROR_ONE_APDU_HEADER = "00DA01E1";
    // "OK" status word sent in response to SELECT AID command
    private  static final  byte[] SELECT_OK_SW = HexStringToByteArray("9000");
    //"UNKNOWN" status word sent in response to invalid APDU command
    private static final byte[] UNKNOWN_CMD_SW = HexStringToByteArray("0000");
    private static final byte[] SELECT_APDU = BuildSelectApdu(LOCKER_READER_AID);
    private static SecurePreferences s_preference;

    @Override
    public void onCreate() {
        super.onCreate(); //call super on create ALWAYS FIRST

        s_preference = new SecurePreferences(this,"msp", Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID), true ); //Initialize secure preferences
        Intent intent = new Intent(this, MainActivity.class);
        intent.setAction(Intent.ACTION_MAIN);
        intent.addCategory(Intent.CATEGORY_LAUNCHER);
        intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(intent);
    }

    /*
    Called if the connection to the NFC card is lost
     */
    @Override
    public void onDeactivated(int reason) {}

    /*
    This method is called when a command APDU has been received from a remote device
     */
    @Override
    public byte[] processCommandApdu(byte[] commandApdu, Bundle extras) {
        //examine the apdu header inorder to determine the command
        switch (getApduHeader(commandApdu))
        {
            case "00A40400":
                String key = s_preference.getString("Key Value");//MainActivity.getKey(); //find the key we have stored
                byte[] keyBytes = key.getBytes(); //convert to byte array
                return ConcatArrays(keyBytes, SELECT_OK_SW); //return the stored key with the ok status word
            case "00DA014B":
                MainActivity.toLockState(getSentKey(commandApdu)); //change to lock state with sent key
                return SELECT_OK_SW;
            case "00DA014F":
                MainActivity.toUnlockState(); //change to unlock state
                return SELECT_OK_SW;
            case "00DA01E0":
                MainActivity.popupError(0); //display error 0 message in popup
                return SELECT_OK_SW;
            case "00DA01E1":
                MainActivity.popupError(1); //display error 1 message in popup
                return SELECT_OK_SW;
            default: return UNKNOWN_CMD_SW; //unknown command

        }
        /*if(Arrays.equals(SELECT_APDU, commandApdu)) {
            String key = MainActivity.getKey();
            byte[] keyBytes = key.getBytes();
            MainActivity.iconImageLock();
            return ConcatArrays(keyBytes, SELECT_OK_SW);
        }
        else
        {
            return  UNKNOWN_CMD_SW;
        }*/
    }

    /*
    Function for examing the Header of an APDU
     */
    private String getApduHeader(byte[] commandApdu) {
        byte[] temp = new byte[4];
        for (int i = 0; i < 4; i++) //The first four bytes of the apdu are the header
        {
            temp[i] = commandApdu[i];
        }
        return bytesToHex(temp);
    }

    /*
    Function for turning a byte array into a hex string
     */
    final private static char[] hexArray = "0123456789ABCDEF".toCharArray(); //table for hex array lookup

    private static String bytesToHex(byte[] bytes) {
        char[] hexChars = new char[bytes.length * 2];
        for ( int j = 0; j < bytes.length; j++ ) {
            int v = bytes[j] & 0xFF;
            hexChars[j * 2] = hexArray[v >>> 4];
            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
        }
        return new String(hexChars);
    }

    /*
    Function for getting the key sent in the apdu header
     */
    private String getSentKey(byte[] commandApdu) {
        byte[] temp = new byte[16];
        int j = 0;
        for (int i = 4; i < 20; i++)
        {
            temp[j] = commandApdu[i];
            j++;
        }
        try
        {
            return new String(temp, "UTF-8");
        }
        catch (IOException e)
        {
            return null;
        }
    }

    /*
    Build APDU for SELECT AID command indicates which service the reader wants
     */
    public static byte[] BuildSelectApdu(String aid) {
        return HexStringToByteArray(SELECT_APDU_HEADER + String.format("%02x",
                aid.length() / 2) + aid);
    }

    /*
    converts a hex string to a byte string
     */
    public static byte[] HexStringToByteArray(String s) throws IllegalArgumentException {
        int len = s.length();
        if(len % 2 == 1) {
            throw new IllegalArgumentException("Hex string must have even number of characters");
        }
        byte[] data = new byte[len / 2]; //Allocates 1 byte per 2 hex chars
        for (int i = 0; i < len; i+=2) {
            data[i/2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16));
        }
        return data;
    }

    /*
    concatenates two byte arrays
     */
    public static byte[] ConcatArrays(byte[] first, byte[]... rest) {
        int totalLength = first.length;
        for (byte[] array : rest) {
            totalLength += array.length;
        }
        byte[] result = Arrays.copyOf(first, totalLength);
        int offset = first.length;
        for (byte[] array : rest) {
            System.arraycopy(array, 0, result, offset, array.length);
            offset += array.length;
        }
        return result;
    }
}
